package com.lagou.edu.factory;

import com.lagou.edu.annotition.MyAutowired;
import com.lagou.edu.annotition.MyComponent;
import com.lagou.edu.annotition.MyTransactional;
import com.lagou.edu.utils.ClassUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 工厂类，生产对象，使用反射技术
 */
public class BeanFactory {

	private static Map<String, Object> map = new HashMap<>();

//	static {
//		//任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
//		//类加载器加载xml
//		InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//		//解析xml
//		SAXReader saxReader = new SAXReader();
//		try {
//			Document document = saxReader.read(resourceAsStream);
//			Element rootElement = document.getRootElement();
//			List<Element> beanList = rootElement.selectNodes("//bean");
//			for (Element element : beanList) {
//				//处理每个bean元素，获取到该元素的id和class属性
//				String id = element.attributeValue("id");
//				String clazz = element.attributeValue("class");
//				//通过反射技术实例化对象
//				Class<?> aClass = Class.forName(clazz);
//				Object o = aClass.newInstance();
//				//存储到map当中待用
//				map.put(id, o);
//			}
//
//			//实例化完成之后维护对象的依赖关系，检查哪些对象需要传值进去，根据它的配置，我们传入相应的值
//			//有property子元素的bean就有传值需求
//			List<Element> propertyList = rootElement.selectNodes("//property");
//			for (Element element : propertyList) {//<property name="AccountDao" ref="accountDao"></property>
//				String name = element.attributeValue("name");
//				String ref = element.attributeValue("ref");
//				//找到当前需要处理依赖关系的bean
//				Element parent = element.getParent();
//				//调用父元素对象的反射功能
//				String parentId = parent.attributeValue("id");
//				Object parentObject = map.get(parentId);
//				//遍历“公用方法”
////				Method[] methods = parentObject.getClass().getMethods();
////				for (Method method : methods) {
////					if (method.getName().equalsIgnoreCase("set" + name)) {//该方法就是setAccountDao
////						method.invoke(parentObject, map.get(ref));
////					}
////				}
//				//改用内省
//				PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, parentObject.getClass());
//				Method writeMethod = propertyDescriptor.getWriteMethod();
//				writeMethod.invoke(parentObject, map.get(ref));
//
//				//把处理之后的parentObject重新放到map中（实践证明，引用传递不需要重新put）
//				//map.put(parentId, parentObject);
//			}
//		} catch (DocumentException | ClassNotFoundException e) {
//		} catch (IllegalAccessException e) {
//			e.printStackTrace();
//		} catch (InstantiationException e) {
//			e.printStackTrace();
//		} catch (InvocationTargetException e) {
//			e.printStackTrace();
//		} catch (IntrospectionException e) {
//			e.printStackTrace();
//		}
//	}

	static {
		InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
		//第一步：解析xml，扫描包路径，实例化bean
		SAXReader saxReader = new SAXReader();
		Document document = null;
		try {
			document = saxReader.read(resourceAsStream);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		Element rootElement = document.getRootElement();
		List<Element> packageList = rootElement.selectNodes("//package");
		String path = packageList.get(0).attributeValue("path");
		List<Class<?>> classes = ClassUtils.getClasses(path);
		for (Class<?> clazz : classes) {
			MyComponent myComponent = clazz.getDeclaredAnnotation(MyComponent.class);
			if (myComponent != null) {
				//取出MyComponent注解的value，作为bean在容器中的key
				String key = myComponent.value();
				Object o = null;
				try {
					o = clazz.newInstance();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				map.put(key, o);
			}
		}

		//第二步：维护对象的依赖关系
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			Object target = entry.getValue();
			Class<? extends Object> clazz = target.getClass();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				MyAutowired myAutowired = field.getDeclaredAnnotation(MyAutowired.class);
				if (myAutowired != null) {
					String name = field.getName();
					Object bean = getBean(name);
					//强制访问
					field.setAccessible(true);
					try {
						field.set(target, bean);
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			}
		}

		//第三步：生成目标对象的代理对象
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			String key = entry.getKey();
			Object target = entry.getValue();
			Class<? extends Object> clazz = target.getClass();
			MyTransactional myTransactional = clazz.getDeclaredAnnotation(MyTransactional.class);
			if (myTransactional != null) {
				ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
				Object proxy;
				//判断target是否代表一个接口类型
				if (clazz.isInterface()) {
					proxy = proxyFactory.getJdkProxy(target);
				} else {
					proxy = proxyFactory.getCglibProxy(target);
				}
				map.put(key, proxy);
			}
		}
	}

	//对外提供获取实例对象的接口
	public static Object getBean(String id) {
		return map.get(id);
	}
}
